<?php

namespace api\components\dataCenter;

use api\modules\app\services\MetaService;
use api\utils\StringUtil;
use app\components\dataCenter\constants\DataField;
use Exception;
use JsonSerializable;
use Yii;
use yii\base\Arrayable;
use yii\helpers\ArrayHelper;

abstract class DataCenterSearchModel implements Arrayable, JsonSerializable
{

    const COMMON_SAVE_URL = '/v2/gateway/generic-meta-status';

    /**
     * 是否为新记录
     * @var bool
     */
    protected $newRecord = true;

    /**
     * @var string
     */
    protected $primeID;

    /**
     * @var array
     */
    protected $args;

    /**
     * @var array
     */
    protected $data;

    /**
     * @var string
     */
    protected $messageType;

    /**
     * 数据中台数据
     * @var array
     */
    protected $origin_data;

    /**
     * @var array
     */
    protected $error = [];

    public static function searchHost()
    {
        return DATA_CENTER_HOST;
    }

    public static function searchToken()
    {
        return DATA_CENTER_TOKEN;
    }

    public static function submitHost()
    {
        return DATA_CENTER_HOST;
    }

    public static function submitToken()
    {
        return DATA_CENTER_SUBMIT_TOKEN;
    }

    protected static function buildSearchEngine()
    {
        return (new SearchEngine(static::tableName()))
            ->modelClass(static::class)
            ->indexType(static::indexType())
            ->setHost(static::searchHost())
            ->setToken(static::searchToken());
    }

    abstract static protected function tableName();

    abstract static protected function indexType();

    public static function find(): SearchEngine
    {
        return static::buildSearchEngine();
    }

    public function __construct($newRecord = true)
    {
        $this->newRecord = $newRecord;
    }

    public function toArray(array $fields = [], array $expand = [], $recursive = true)
    {
        return [
            'primeID' => $this->primeID,
            'args' => $this->args,
            'data' => $this->data,
            'messageType' => $this->messageType
        ];
    }

    function jsonSerialize(): array
    {
        return $this->toArray();
    }

    /**
     * @param int $options
     *
     * @return string
     */
    public function toJson(int $options = JSON_UNESCAPED_UNICODE): string
    {
        return \json_encode($this->jsonSerialize(), $options);
    }

    /**
     * @param array $data
     * @param bool|null $newRecord
     * @return null
     * @throws Exception
     */
    public function fill(array $data = [], ?bool $newRecord = null)
    {
        if (empty($data)) return null;

        if (!isset($data['primeID']) || !isset($data['args']) || !isset($data['data']) || !isset($data['messageType'])) {
            throw new Exception('数据格式错误');
        }
        $this->primeID = $data['primeID'];
        $this->args = $data['args'];
        $this->data = $data['data'];
        $this->messageType = $data['messageType'];
        $this->origin_data = $data;
        isset($newRecord) && $this->newRecord = $newRecord;
        return $this;
    }

    public function isNewRecord(?bool $newRecord = null)
    {
        isset($newRecord) && $this->newRecord = $newRecord;
        return $this->newRecord;
    }

    /**
     * @param string $primeID
     * @return $this
     * @throws Exception
     */
    public function setPrimeID(string $primeID)
    {
        if (!$this->newRecord) {
            throw new Exception('无法修改主键');
        }
        $this->primeID = $primeID;
        return $this;
    }

    public function setData(array $data)
    {
        $this->data = $data;
        return $this;
    }

    public function setArgs(array $args)
    {
        $this->args = $args;
        return $this;
    }

    public function setMessageType(string $message_type)
    {
        $this->messageType = $message_type;
        return $this;
    }

    /**
     * @param string $colume
     * @param $value
     * @param string|null $channel
     * @return DataCenterSearchModel
     */
    public function setValue(string $colume, $value, ?string $channel = null)
    {
        if ($channel && isset($this->$channel)) {
            ArrayHelper::setValue($this->$channel, $colume, $value);
        } else {
            $level_colume = explode('.', $colume);
            if (!isset($this->{$level_colume[0]})) {
                $channel = array_shift($level_colume);
                ArrayHelper::setValue($this->$channel, implode('.', $level_colume), $value);
            } else {
                ArrayHelper::setValue($this->data, $colume, $value);
            }
        }
        return $this;
    }

    /**
     * 获取字段值
     * 多层级使用 “ . ” 来区分
     * 例如 data.town.areaName 表示data下的town字段下的areaName字段
     * @param string $colume
     * @param string|null $channel
     * @return array|null
     */
    public function getValue(string $colume, ?string $channel = null)
    {
        $level_colume = explode('.', $colume);

        if ($channel && isset($this->$channel)) {
            $data = $this->$channel;
        } else {
            if (!isset($this->{$level_colume[0]})) {
                $data = $this->data;
            } else {
                $data = $this->{$level_colume[0]};
                array_shift($level_colume);
            }
        }
        while (isset($data) && !empty($level_colume)) {
            $data = $data[array_shift($level_colume)] ?? null;
        }
        return $data;
    }

    public static function handleData(array $row, array $export_field, $with_id = false): array
    {
        $return = [];
        foreach ($export_field as $field) {
            if (!isset($field['field']) || empty($field['field'])) {
                $value = $field['default'] ?? '';
            } elseif (is_array($field['field'])) {
                $value = [];
                foreach ($field['field'] as $each_field) {
                    $value[$each_field] = ArrayHelper::getValue($row, $each_field, $field['default'] ?? null);
                }
            } else {
                $value = ArrayHelper::getValue($row, $field['field'], $field['default'] ?? null);
            }
            // 设置了 formatter 格式化方法的，先走formatter方法
            if (isset($field['formatter'])) {
                $value = self::formatValue($value, $field['formatter'], $field['options'], $row);
            }
            // 设置了 map 获取map中的值
            // 注意 若为多字段 则要分别取map中的值
            if (isset($value) && isset($field['map']) && is_array($field['map'])) {
                if (is_array($value)) {
                    $value = array_map(
                        function ($value) use ($field) {
                            if (!is_numeric($value) && strpos(':', $value)) {
                                $values = explode(':', $value);
                                $value = $values[0];
                            }
                            return $field['map'][$value] ?? ($field['default'] ?? null);
                        }, $value);
                } else {
                    if (!is_numeric($value) && !StringUtil::isEmpty($value) && strpos(':', $value)) {
                        $values = explode(':', $value);
                        $value = $values[0];
                    }
                    $value = $field['map'][$value] ?? ($field['default'] ?? null);
                }
            }
            //若设置了data_type 则根据不同的data_type，进行不同的转换方法
            switch ($field['data_type'] ?? null) {
                case DataField::FIELD_TYPE_TIME:
                    $value = is_numeric($value) ? date('Y-m-d H:i:s', $value) : $value;
                    break;
                case DataField::FIELD_TYPE_AREA:
                    $value = $value['areaName'];
                    break;
                case DataField::FIELD_TYPE_GEO_LOCATION:
                    $value = $value['latitude'] . ',' . $value['longitude'];
                    break;
                default:
                    break;
            }
            $value = is_null($value)
                ? ($field['default'] ?? '') : (is_array($value) ? implode(',', $value) : strval($value));

            if (is_numeric($value)) {
                $value = chr(0x1E) . $value; // 增加特殊字符，显示 021
            }
            if (in_array($field['id'], ['402', '403']) && in_array(ArrayHelper::getValue($row, 'data.showStatus'), ['立案','派遣', '处置'])) {
                $value = ''; // TODO 逆向操作后 处理结果，处理意见 不好清除，暂时隐藏。
            }
            (isset($field['id']) && $with_id) ? $return[$field['id']] = $value : $return[] = $value;
        }
        return $return;
    }

    public static function formatValue($value, $funcName, $options, $data)
    {
        if (!method_exists(self::class, $funcName)) {
            return $value;
        }

        return self::$funcName($value, $options, $data);
    }

    public static function translateCode($key, $options = '', $row = [])
    {
        $data = (new MetaService())->getCodeDict();
        return $data[$key] ?? $key;
    }

    public static function exportString($value, $options = '', $row = [])
    {
        return isset($value) ? $value . chr(0x1E) : '';
    }

    public static function translateBool($value, $options = '', $row = [])
    {
        return isset($value) ? ($value == 1 ? '是' : '否') : '';
    }

    public static function translateTimestamp($value, $options = '', $row = [])
    {
        return !empty($value) ? date('Y-m-d H:i:s', $value) : '';
    }

    public static function getFromMulitKey($value, $options = '', $row = [])
    {
        $options = explode(',', $options);
        foreach ($options as $key) {
            if (!empty($row[$key])) {
                return $row[$key];
            }
        }
    }

    protected function buildSaveData()
    {
        $data = [
            'primeID' => $this->primeID,
            'data' => $this->data,
            'messageType' => $this->messageType
        ];
        !empty($this->args) && $data['args'] = $this->args;
        return $data;
    }

    protected function beforeSave()
    {
        return true;
    }

    /**
     * 保存至数据中台
     * @param int $save_user 操作人
     * @return bool
     * @throws Exception
     */
    public function save($save_user = 0)
    {
        if (StringUtil::isEmpty($this->primeID)) {
            throw new Exception('primeID不能为空');
        }
        if ($this->beforeSave() || !empty($this->error)) {
            if ($this->isNewRecord()) {
                $this->data['createTime'] = time();
                $save_user && $this->data['createUser'] = $save_user;
            } else {
                $this->data['updateTime'] = time();
                $save_user && $this->data['updateUser'] = $save_user;
            }
            $send_data = $this->buildSaveData();

            $url = self::submitHost() . self::COMMON_SAVE_URL . '?token=' . self::submitToken();
            $data = Yii::$app->curl->post($url, $send_data);
            if (isset($data['result']) && $data['result'] == 'success') {
                return true;
            } else {
                $this->error[] = $data['result'] ?? '数据中台返回错误 res:' . json_encode($data);
                return false;
            }
        }
        return false;
    }

    public function getError()
    {
        return $this->error;
    }

    public function getErrorMsg()
    {
        return reset($this->error);
    }

}
